import processing.core.PVector;
import java.lang.Math.*;

public class Matrix4 {

  float[][] data;

  public Matrix4() {
    data = new float[3][3];
    for (int i=0; i < 3; i++) {
      for (int j=0; j < 3; j++) {
        data[i][j] = 0;
      }
    }
  }

  public PVector multiplyVector(PVector vector) {
    PVector ret = new PVector();
    ret.x = data[0][0] * vector.x + data[0][1] * vector.y + data[0][2] * vector.z;
    ret.y = data[1][0] * vector.x + data[1][1] * vector.y + data[1][2] * vector.z;
    ret.z = data[2][0] * vector.x + data[2][1] * vector.y + data[2][2] * vector.z;
    return ret;
  }

  public Matrix4 multiplyMatrix(Matrix4 matrix) {
    Matrix4 ret = new Matrix4();
    for (int i = 0; i < size(); i++) {
      for (int j=0; j < size(); j++) {
        for (int k=0; k < size(); k++) {
          ret.data[i][j] += data[i][k] * matrix.data[k][j];
        }
      }
    }
    return ret;
  }

  public int size() {
    return data.length;
  }

  public static Matrix4 rotation(float angle) {
    Matrix4 rotation = new Matrix4();
    rotation.set(0, 0, (float)Math.cos(angle));
    rotation.set(0, 1, -1.0f*(float)Math.sin(angle));
    rotation.set(1, 0, (float)Math.sin(angle));
    rotation.set(1, 1, (float)Math.cos(angle));
    rotation.set(2, 2, 1.0f);
    return rotation;
  } 

  public static Matrix4 translation(float dx, float dy) {
    Matrix4 translation = new Matrix4();
    translation.set(0, 0, 1.0f);
    translation.set(1, 1, 1.0f);
    translation.set(2, 2, 1.0f);
    translation.set(0, 2, dx);
    translation.set(1, 2, dy);
    return translation;
  }

  public static Matrix4 scaling(float percentageX, float percentageY) {
    Matrix4 scaling = new Matrix4();
    scaling.set(0, 0, percentageX);
    scaling.set(1, 1, percentageY);
    scaling.set(2, 2, 1.0f);
    return scaling;
  }

  private void set(int i, int j, float val) {
    data[i][j] = val;
  }

  public static Matrix4 referenceScaling(float percentageX, float percentageY, PVector referencePoint) {
    Matrix4 transformation = Matrix4.translation(referencePoint.x, referencePoint.y);
    transformation = transformation.multiplyMatrix(Matrix4.scaling(percentageX, percentageY));
    transformation = transformation.multiplyMatrix(Matrix4.translation(-1.0f*referencePoint.x, -1.0f*referencePoint.y));
    return transformation;
  }
  
  public static Matrix4 referenceRotation(float angle, PVector referencePoint) {
    Matrix4 transformation = Matrix4.translation(referencePoint.x, referencePoint.y);
    transformation = transformation.multiplyMatrix(Matrix4.rotation(angle));
    transformation = transformation.multiplyMatrix(Matrix4.translation(-1.0f*referencePoint.x, -1.0f*referencePoint.y));
    return transformation;
  }
}

